home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Tools 2
/
Amiga Tools 2.iso
/
tools
/
packer
/
tar
/
src
/
tar.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-03-09
|
12KB
|
527 lines
/*
* A public domain tar(1) program.
*
* Written by John Gilmore, ihnp4!hoptoad!gnu, starting 25 Aug 85.
*
* @(#)tar.c 1.34 11/6/87 Public Domain - gnu
*/
#include <stdio.h>
#include <sys/types.h> /* Needed for typedefs in tar.h */
extern char *malloc();
extern char *getenv();
extern char *strncpy();
extern char *optarg; /* Pointer to argument */
extern int optind; /* Global argv index from getopt */
/*
* The following causes "tar.h" to produce definitions of all the
* global variables, rather than just "extern" declarations of them.
*/
#define TAR_EXTERN /**/
#include "tar.h"
/*
* We should use a conversion routine that does reasonable error
* checking -- atoi doesn't. For now, punt. FIXME.
*/
#define intconv atoi
extern int getoldopt();
extern void read_and();
extern void list_archive();
extern void extract_archive();
extern void diff_archive();
extern void create_archive();
static FILE *namef; /* File to read names from */
static char **n_argv; /* Argv used by name routines */
static int n_argc; /* Argc used by name routines */
/* They also use "optind" from getopt(). */
void describe();
#ifdef AMIGA
extern char *_TZ; /* timezone stuff */
#endif
/*
* Main routine for tar.
*/
main(argc, argv)
int argc;
char **argv;
{
/* Uncomment this message in particularly buggy versions...
fprintf(stderr,
"tar: You are running an experimental PD tar, maybe use /bin/tar.\n");
*/
tar = "tar"; /* Set program name */
options(argc, argv);
name_init(argc, argv);
if (f_create) {
if (f_extract || f_list || f_diff) goto dupflags;
create_archive();
} else if (f_extract) {
if (f_list || f_diff) goto dupflags;
extr_init();
read_and(extract_archive);
} else if (f_list) {
if (f_diff) goto dupflags;
read_and(list_archive);
} else if (f_diff) {
diff_init();
read_and(diff_archive);
} else {
dupflags:
fprintf (stderr,
"tar: you must specify exactly one of the c, t, x, or d options\n");
describe();
exit(EX_ARGSBAD);
}
exit(0);
/* NOTREACHED */
}
/*
* Parse the options for tar.
*/
int
options(argc, argv)
int argc;
char **argv;
{
register int c; /* Option letter */
#ifdef AMIGA
char *timezone;
#endif
/* Set default option values */
blocking = DEFBLOCKING; /* From Makefile */
ar_file = getenv("TAPE"); /* From environment, or */
#ifdef AMIGA
timezone = getenv("TZ");
if (timezone)
{
_TZ = malloc(strlen(timezone) + 1); /* man page unclear about */
strcpy(_TZ, timezone); /* whether this is needed */
free(timezone);
}
else
_TZ = "CST6CDT";
tzset();
#endif
if (ar_file == 0)
#ifdef AMIGA
ar_file = "ram:tarfile"; /* From Makefile */
#else
ar_file = DEF_AR_FILE; /* From Makefile */
#endif
/* Parse options */
#ifdef AMIGA
while ((c = getoldopt(argc, argv, "aAb:BcdDf:hiklmopRstT:vxzZ")
#else
while ((c = getoldopt(argc, argv, "b:BcdDf:hiklmopRstT:vxzZ")
#endif
) != EOF) {
switch (c) {
#ifdef AMIGA
case 'a':
f_archive_set++;
break;
case 'A':
f_archive_check++;
break;
#endif
case 'b':
blocking = intconv(optarg);
break;
case 'B':
f_reblock++; /* For reading 4.2BSD pipes */
break;
case 'c':
f_create++;
break;
case 'd':
f_diff++; /* Find difference tape/disk */
break;
case 'D':
f_dironly++; /* Dump dir, not contents */
break;
case 'f':
ar_file = optarg;
break;
case 'h':
f_follow_links++; /* follow symbolic links */
break;
case 'i':
f_ignorez++; /* Ignore zero records (eofs) */
/*
* This can't be the default, because Unix tar
* writes two records of zeros, then pads out the
* block with garbage.
*/
break;
case 'k': /* Don't overwrite files */
#ifdef NO_OPEN3
fprintf(stderr,
"tar: can't do -k option on this system\n");
exit(EX_ARGSBAD);
#else
f_keep++;
#endif
break;
case 'l':
f_local_filesys++;
break;
case 'm':
f_modified++;
break;
case 'o': /* Generate old archive */
f_oldarch++;
break;
case 'p':
f_use_protection++;
break;
case 'R':
f_sayblock++; /* Print block #s for debug */
break; /* of bad tar archives */
case 's':
f_sorted_names++; /* Names to extr are sorted */
break;
case 't':
f_list++;
f_verbose++; /* "t" output == "cv" or "xv" */
break;
case 'T':
name_file = optarg;
f_namefile++;
break;
case 'v':
f_verbose++;
break;
case 'x':
f_extract++;
break;
case 'z': /* Easy to type */
case 'Z': /* Like the filename extension .Z */
f_compress++;
break;
case '?':
describe();
exit(EX_ARGSBAD);
}
}
blocksize = blocking * RECORDSIZE;
}
/*
* Print as much help as the user's gonna get.
*
* We have to sprinkle in the KLUDGE lines because too many compilers
* cannot handle character strings longer than about 512 bytes. Yuk!
* In particular, MSDOS MSC 4.0 (and 5.0) and PDP-11 V7 Unix have this
* problem.
*/
void
describe()
{
/* sorry, can't #ifdef AMIGA inside string */
fputs("\
tar: valid options:\n\
-a set the archived bit of each file as it's added to the archive\n\
-b N blocking factor N (block size = Nx512 bytes)\n\
-B reblock as we read (for reading 4.2BSD pipes)\n\
-c create an archive\n\
-d find differences between archive and file system\n\
-D don't dump the contents of directories, just the directory\n\
", stderr); /* KLUDGE */ fputs("\
-f F read/write archive from file or device F (or hostname:/ForD)\n\
-h don't dump symbolic links; dump the files they point to\n\
-i ignore blocks of zeros in the archive, which normally mean EOF\n\
-k keep existing files, don't overwrite them from the archive\n\
-l stay in the local file system (like dump(8)) when creating an archive\n\
", stderr); /* KLUDGE */ fputs("\
-m don't extract file modified time\n\
-o write an old V7 format archive, rather than ANSI [draft 6] format\n\
-p do extract all protection information\n\
-R dump record number within archive with each message\n\
-s list of names to extract is sorted to match the archive\n\
-t list a table of contents of an archive\n\
", stderr); /* KLUDGE */ fputs("\
-T F get names to extract or create from file F\n\
-v verbosely list what files we process\n\
-x extract files from an archive\n\
-z or Z run the archive through compress(1)\n\
", stderr);
}
/*
* Set up to gather file names for tar.
*
* They can either come from stdin or from argv.
*/
name_init(argc, argv)
int argc;
char **argv;
{
if (f_namefile) {
if (optind < argc) {
fprintf(stderr, "tar: too many args with -T option\n");
exit(EX_ARGSBAD);
}
if (!strcmp(name_file, "-")) {
namef = stdin;
} else {
namef = fopen(name_file, "r");
if (namef == NULL) {
fprintf(stderr, "tar: ");
perror(name_file);
exit(EX_BADFILE);
}
}
} else {
/* Get file names from argv, after options. */
n_argc = argc;
n_argv = argv;
}
}
/*
* Get the next name from argv or the name file.
*
* Result is in static storage and can't be relied upon across two calls.
*/
char *
name_next()
{
static char buffer[NAMSIZ+2]; /* Holding pattern */
register char *p;
register char *q;
if (namef == NULL) {
/* Names come from argv, after options */
if (optind < n_argc)
return n_argv[optind++];
return (char *)NULL;
}
for (;;) {
p = fgets(buffer, NAMSIZ+1 /*nl*/, namef);
if (p == NULL) return p; /* End of file */
q = p+strlen(p)-1; /* Find the newline */
if (q <= p) continue; /* Ignore empty lines */
*q-- = '\0'; /* Zap the newline */
while (q > p && *q == '/') *q-- = '\0'; /* Zap trailing /s */
return p;
}
/* NOTREACHED */
}
/*
* Close the name file, if any.
*/
name_close()
{
if (namef != NULL && namef != stdin) fclose(namef);
}
/*
* Gather names in a list for scanning.
* Could hash them later if we really care.
*
* If the names are already sorted to match the archive, we just
* read them one by one. name_gather reads the first one, and it
* is called by name_match as appropriate to read the next ones.
* At EOF, the last name read is just left in the buffer.
* This option lets users of small machines extract an arbitrary
* number of files by doing "tar t" and editing down the list of files.
*/
name_gather()
{
register char *p;
static struct name namebuf[1]; /* One-name buffer */
if (f_sorted_names) {
p = name_next();
if (p) {
namebuf[0].length = strlen(p);
if (namebuf[0].length >= sizeof namebuf[0].name) {
fprintf(stderr, "Argument name too long: %s\n",
p);
namebuf[0].length = (sizeof namebuf[0].name) - 1;
}
strncpy(namebuf[0].name, p, namebuf[0].length);
namebuf[0].name[ namebuf[0].length ] = 0;
namebuf[0].next = (struct name *)NULL;
namebuf[0].found = 0;
namelist = namebuf;
namelast = namelist;
}
return;
}
/* Non sorted names -- read them all in */
while (NULL != (p = name_next())) {
addname(p);
}
}
/*
* Add a name to the namelist.
*/
addname(name)
char *name; /* pointer to name */
{
register int i; /* Length of string */
register struct name *p; /* Current struct pointer */
i = strlen(name);
/*NOSTRICT*/
p = (struct name *)
malloc((unsigned)(i + sizeof(struct name) - NAMSIZ));
if (!p) {
fprintf(stderr,"tar: cannot allocate mem for namelist entry\n");
exit(EX_SYSTEM);
}
p->next = (struct name *)NULL;
p->length = i;
strncpy(p->name, name, i);
p->name[i] = '\0'; /* Null term */
p->found = 0;
p->regexp = 0; /* Assume not a regular expression */
p->firstch = 1; /* Assume first char is literal */
if (index(name, '*') || index(name, '[') || index(name, '?')) {
p->regexp = 1; /* No, it's a regexp */
if (name[0] == '*' || name[0] == '[' || name[0] == '?')
p->firstch = 0; /* Not even 1st char literal */
}
if (namelast) namelast->next = p;
namelast = p;
if (!namelist) namelist = p;
}
/*
* Match a name from an archive, p, with a name from the namelist.
*/
name_match(p)
register char *p;
{
register struct name *nlp;
register int len;
again:
if (0 == (nlp = namelist)) /* Empty namelist is easy */
return 1;
len = strlen(p);
for (; nlp != 0; nlp = nlp->next) {
/* If first chars don't match, quick skip */
if (nlp->firstch && nlp->name[0] != p[0])
continue;
/* Regular expressions */
if (nlp->regexp) {
if (wildmat(p, nlp->name)) {
nlp->found = 1; /* Remember it matched */
return 1; /* We got a match */
}
continue;
}
/* Plain Old Strings */
if (nlp->length <= len /* Archive len >= specified */
&& (p[nlp->length] == '\0' || p[nlp->length] == '/')
/* Full match on file/dirname */
&& strncmp(p, nlp->name, nlp->length) == 0) /* Name compare */
{
nlp->found = 1; /* Remember it matched */
return 1; /* We got a match */
}
}
/*
* Filename from archive not found in namelist.
* If we have the whole namelist here, just return 0.
* Otherwise, read the next name in and compare it.
* If this was the last name, namelist->found will remain on.
* If not, we loop to compare the newly read name.
*/
if (f_sorted_names && namelist->found) {
name_gather(); /* Read one more */
if (!namelist->found) goto again;
}
return 0;
}
/*
* Print the names of things in the namelist that were not matched.
*/
names_notfound()
{
register struct name *nlp;
register char *p;
for (nlp = namelist; nlp != 0; nlp = nlp->next) {
if (!nlp->found) {
fprintf(stderr, "tar: %s not found in archive\n",
nlp->name);
}
/*
* We could free() the list, but the process is about
* to die anyway, so save some CPU time. Amigas and
* other similarly broken software will need to waste
* the time, though.
*/
#ifndef unix
if (!f_sorted_names)
free(nlp);
#endif
}
namelist = (struct name *)NULL;
namelast = (struct name *)NULL;
if (f_sorted_names) {
while (0 != (p = name_next()))
fprintf(stderr, "tar: %s not found in archive\n", p);
}
}